/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.meta.service.common.impl;

import com.je.common.base.DynaBean;
import com.je.common.base.JsonBuilder;
import com.je.common.base.constants.table.ColumnType;
import com.je.common.base.constants.tree.TreeNodeType;
import com.je.common.base.entity.func.FuncRelationField;
import com.je.common.base.exception.PlatformException;
import com.je.common.base.exception.PlatformExceptionEnum;
import com.je.common.base.service.MetaService;
import com.je.common.base.service.rpc.BeanService;
import com.je.common.base.util.ArrayUtils;
import com.je.common.base.util.StringUtil;
import com.je.core.entity.extjs.JSONTreeNode;
import com.je.ibatis.extension.conditions.ConditionsWrapper;
import com.je.meta.cache.table.TableCache;
import com.je.meta.service.common.MetaBeanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class MetaBeanServiceImpl implements MetaBeanService {

    @Autowired
    private MetaService metaService;

    @Autowired
    private TableCache tableCacheService;

    @Override
    public DynaBean getResourceTable(String tableCode) {

        if (tableCacheService.getCacheValue("JE_CORE_RESOURCETABLE") == null) {
            //初始化资源表模版
            DynaBean resourceTable = initSysTable("JE_CORE_RESOURCETABLE");
            tableCacheService.putCache("JE_CORE_RESOURCETABLE", resourceTable);
        }
        if (tableCacheService.getCacheValue("JE_CORE_TABLECOLUMN") == null) {
            DynaBean tableColumn = initSysTable("JE_CORE_TABLECOLUMN");
            tableCacheService.putCache("JE_CORE_TABLECOLUMN", tableColumn);
        }
        if (tableCacheService.getCacheValue("JE_CORE_TABLEKEY") == null) {
            DynaBean tableKey = initSysTable("JE_CORE_TABLEKEY");
            tableCacheService.putCache("JE_CORE_TABLEKEY", tableKey);
        }
        if (tableCacheService.getCacheValue("JE_CORE_TABLEINDEX") == null) {
            DynaBean tableIndex = initSysTable("JE_CORE_TABLEINDEX");
            tableCacheService.putCache("JE_CORE_TABLEINDEX", tableIndex);
        }

        DynaBean table = tableCacheService.getCacheValue(tableCode);
        if (table != null) {
            return table;
        }

        table = metaService.selectOne("JE_CORE_RESOURCETABLE", ConditionsWrapper.builder()
                .eq("RESOURCETABLE_TABLECODE", tableCode)
                .ne("RESOURCETABLE_TYPE", "MODULE"), getNoClobQueryFields("JE_CORE_RESOURCETABLE"));

        List<DynaBean> columns = metaService.select("JE_CORE_TABLECOLUMN", ConditionsWrapper.builder()
                .eq("TABLECOLUMN_RESOURCETABLE_ID", table.getStr("JE_CORE_RESOURCETABLE_ID"))
                .eq("TABLECOLUMN_ISCREATE", "1")
                .orderByAsc("TABLECOLUMN_CLASSIFY", "TABLECOLUMN_CODE"));

        List<DynaBean> keys = metaService.select("JE_CORE_TABLEKEY", ConditionsWrapper.builder()
                .eq("TABLEKEY_RESOURCETABLE_ID", table.getStr("JE_CORE_RESOURCETABLE_ID"))
                .eq("TABLEKEY_ISCREATE", "1")
                .orderByAsc("SY_ORDERINDEX"));

        List<DynaBean> indexs = metaService.select("JE_CORE_TABLEINDEX", ConditionsWrapper.builder()
                .eq("TABLEINDEX_RESOURCETABLE_ID", table.getStr("JE_CORE_RESOURCETABLE_ID"))
                .eq("TABLEINDEX_ISCREATE", "1")
                .orderByAsc("SY_ORDERINDEX"));
        table.set(BeanService.KEY_TABLE_COLUMNS, columns);
        table.set(BeanService.KEY_TABLE_KEYS, keys);
        table.set(BeanService.KEY_TABLE_INDEXS, indexs);
        table.set(BeanService.KEY_PK_CODE, tableCode);

        for (DynaBean key : keys) {
            //本系统目前不支持符合主键 2012-5-2
            //研发部 : 云凤程
            if ("Primary".equals(key.getStr("TABLEKEY_TYPE"))) {
                String tablePK = key.getStr("TABLEKEY_COLUMNCODE");
                //同时装载到bean的特殊字段上
                table.setStr(BeanService.KEY_PK_CODE, tablePK);
                break;
            }
        }

        table.set(BeanService.KEY_TABLE_CODE, tableCode);
        tableCacheService.putCache(tableCode, table);
        return table;
    }

    @Override
    public String getPKeyFieldNames(DynaBean dynaBean) {
        String tablePK = "";
        String tableCode = dynaBean.getStr(BeanService.KEY_TABLE_CODE);
        DynaBean table = getResourceTable(tableCode);
        if (null == table) {
            throw new PlatformException("数据表:" + tableCode + " 未找到", PlatformExceptionEnum.JE_CORE_DYNABEAN_ERROR, new Object[]{dynaBean});
        }

        for (DynaBean key : (List<DynaBean>) table.get(BeanService.KEY_TABLE_KEYS)) {
            //本系统目前不支持符合主键 2012-5-2
            //研发部 : 云凤程
            if ("Primary".equals(key.getStr("TABLEKEY_TYPE"))) {
                tablePK = key.getStr("TABLEKEY_COLUMNCODE");
                //同时装载到bean的特殊字段上
                dynaBean.setStr(BeanService.KEY_PK_CODE, tablePK);
                break;
            }
        }

        return tablePK;
    }

    @Override
    public String getPKeyFieldNames(String tableCode) {
        String tablePK = "";
        DynaBean table = getResourceTable(tableCode);

        if (null == table) {
            throw new PlatformException("数据表:" + tableCode + " 未找到", PlatformExceptionEnum.JE_CORE_DYNABEAN_ERROR, new Object[]{tableCode});
        }

        for (DynaBean key : (List<DynaBean>) table.get(BeanService.KEY_TABLE_KEYS)) {
            //本系统目前不支持符合主键 2012-5-2
            //研发部 : 云凤程
            if ("Primary".equals(key.getStr("TABLEKEY_TYPE"))) {
                tablePK = key.getStr("TABLEKEY_COLUMNCODE");
                break;
            }
        }

        return tablePK;
    }

    @Override
    public String getForeignKeyField(String tableCode, String parentTableCode, String parentPkCode, List<FuncRelationField> relatedFields) {
        DynaBean resourceTable = getResourceTable(tableCode);
        String foreignKey = "";

        //找到外键关联并构建删除条件
        List<DynaBean> keys = (List<DynaBean>) resourceTable.get(BeanService.KEY_TABLE_KEYS);
        for (DynaBean key : keys) {
            if ("Foreign".equals(key.getStr("TABLEKEY_TYPE")) && parentTableCode.equals(key.getStr("TABLEKEY_LINKTABLE")) && parentPkCode.equals(key.getStr("TABLEKEY_LINECOLUMNCODE"))) {
                foreignKey = key.getStr("TABLEKEY_COLUMNCODE");
                break;
            }
        }

        //如果资源表无主外关系则在关联字段中加入
        if (StringUtil.isEmpty(foreignKey)) {
            for (int i = 0; i < relatedFields.size(); i++) {
                FuncRelationField funcRelationField = relatedFields.get(i);
                if (parentPkCode.equals(funcRelationField.getFieldCode())) {
                    foreignKey = funcRelationField.getChildFieldCode();
                    break;
                }
            }
        }

        return foreignKey;
    }

    @Override
    public String[] getNames(DynaBean dynaBean) {
        HashMap valueMap = dynaBean.getValues();
        String[] names = new String[valueMap.size()];
        int i = 0;
        for (Iterator it = valueMap.keySet().iterator(); it.hasNext(); i++) {
            names[i] = (String) it.next();
        }
        return names;
    }

    @Override
    public String getNames4Sql(DynaBean table) {
        StringBuilder names = new StringBuilder();
        //根据ResourceTable得到DyanBean
        DynaBean dynaBean = getDynaBeanByResourceTable(table);
        String[] arrayName = getNames(dynaBean);
        for (String name : arrayName) {
            if (!name.startsWith(StringUtil.DOLLAR)) {
                names.append(name + ",");
            }
        }
        names.setLength(names.length() - 1);
        return names.toString();
    }

    @Override
    public String getUpdateInfos4Sql(DynaBean resourceTable, Map values) {
        StringBuilder names = new StringBuilder();
        DynaBean dynaBean = getDynaBeanByResourceTable(resourceTable);
        String[] arrayName = getNames(dynaBean);
        for (String name : arrayName) {
            if (!name.startsWith(StringUtil.DOLLAR)) {
                if (values.get(name) != null) {
                    names.append(name + "=:" + name + ",");
                }
            }
        }
        names.setLength(names.length() - 1);
        return names.toString();
    }

    @Override
    public String getNames2DynaBean4Sql(DynaBean dynaBean) {
        StringBuilder names = new StringBuilder();
        String[] arrayName = getNames(dynaBean);
        for (String name : arrayName) {
            if (!name.startsWith(StringUtil.DOLLAR)) {
                names.append(name + ",");
            }
        }
        names.setLength(names.length() - 1);
        return names.toString();
    }

    @Override
    public String getValues4Sql(DynaBean table) {
        StringBuilder values = new StringBuilder();
        //根据ResourceTable得到DyanBean
        DynaBean dynaBean = getDynaBeanByResourceTable(table);
        String[] arrayName = getNames(dynaBean);
        for (String name : arrayName) {
            if (!name.startsWith(StringUtil.DOLLAR)) {
                values.append(":" + name + ",");
            }
        }
        values.setLength(values.length() - 1);
        return values.toString();
    }

    @Override
    public DynaBean getDynaBeanByResourceTable(DynaBean table) {
        DynaBean dynaBean = new DynaBean(table.getStr("RESOURCETABLE_TABLECODE"));
        List<DynaBean> columns = (List<DynaBean>) table.get(BeanService.KEY_TABLE_COLUMNS);
        for (DynaBean column : columns) {
            dynaBean.set(column.getStr("TABLECOLUMN_CODE"), "");
        }
        return dynaBean;
    }

    @Override
    public Object[] getValues(DynaBean dynaBean) {
        HashMap valueMap = dynaBean.getValues();
        Object[] values = new String[valueMap.size()];
        int i = 0;
        for (Iterator it = valueMap.keySet().iterator(); it.hasNext(); i++) {
            values[i] = valueMap.get((String) it.next());
        }
        return values;
    }

    /**
     * 得到动态类的内容转为字符串。
     *
     * @param dynaBean 动态类
     * @return 动态类对应的字符串内容
     */
    @SuppressWarnings("rawtypes")
    public static String toString(DynaBean dynaBean) {
        HashMap valueMap = dynaBean.getValues();
        StringBuffer sb = new StringBuffer(valueMap.size() * 4);
        String name;
        for (Iterator it = valueMap.keySet().iterator(); it.hasNext(); ) {
            name = (String) it.next();
            sb.append(name).append("=").append(valueMap.get(name)).append(";");
        }
        return sb.toString();
    }

    @Override
    public List<DynaBean> buildUpdateList(String updateStr, String tableCode) {
        List<DynaBean> beans = new ArrayList<DynaBean>();
        if (StringUtil.isEmpty(updateStr)) {
            return beans;
        }
        List<Map> sqlMapList = JsonBuilder.getInstance().fromJsonArray(updateStr);
        for (int i = 0; i < sqlMapList.size(); i++) {
            Map sqlMap = sqlMapList.get(i);
            DynaBean dynaBean = new DynaBean(tableCode, true);
            for (Object obj : sqlMap.entrySet()) {
                Map.Entry entry = (Map.Entry) obj;
                String k = StringUtil.getDefaultValue(entry.getKey(), "");
                if (StringUtil.isNotEmpty(k)) {
                    if (sqlMap.get(k) instanceof String && sqlMap.get(k).equals("null")) {
                        dynaBean.set(k, null);
                    } else {
                        dynaBean.set(k, sqlMap.get(k));
                    }
                }
            }
            beans.add(dynaBean);
        }
        return beans;
    }

    @Override
    public String[] getDynaBeanIdArray(List<DynaBean> beans, String pkCode) {
        String[] idArray = new String[beans.size()];
        for (Integer i = 0; i < beans.size(); i++) {
            idArray[i] = beans.get(i).getStr(pkCode, "");
        }
        return idArray;
    }

    @Override
    @Transactional
    public DynaBean initSysTable(String tableCode) {
        String tableQueryFields = "RESOURCETABLE_CHILDTABLECODES,RESOURCETABLE_ICON,RESOURCETABLE_IMPLWF,RESOURCETABLE_ISCREATE,RESOURCETABLE_ISUSEFOREIGNKEY,RESOURCETABLE_MOREROOT,RESOURCETABLE_NODEINFO,RESOURCETABLE_NODEINFOTYPE,RESOURCETABLE_OLDTABLECODE,RESOURCETABLE_PARENTTABLECODES,RESOURCETABLE_PKCODE,RESOURCETABLE_REMARK,RESOURCETABLE_TABLECODE,RESOURCETABLE_TABLENAME,RESOURCETABLE_TABLENOTE,RESOURCETABLE_TYPE,RESOURCETABLE_USEFUNC,SY_DISABLED,SY_JECORE,SY_JESYS,JE_CORE_RESOURCETABLE_ID,SY_AUDFLAG,SY_CREATEORGID,SY_CREATEORGNAME,SY_CREATETIME,SY_CREATEUSERID,SY_CREATEUSERNAME,SY_FLAG,SY_LAYER,SY_MODIFYORGID,SY_MODIFYORGNAME,SY_MODIFYTIME,SY_MODIFYUSERID,SY_MODIFYUSERNAME,SY_NODETYPE,SY_ORDERINDEX,SY_PARENT,SY_PARENTPATH,SY_PATH,SY_PDID,SY_PIID,SY_STATUS,SY_TREEORDERINDEX";
        List<Map<String, Object>> lists = metaService.selectSql("SELECT " + tableQueryFields + " FROM JE_CORE_RESOURCETABLE WHERE RESOURCETABLE_TABLECODE='" + tableCode + "'");
        Map tableVals = lists.get(0);
        DynaBean resourceTable = new DynaBean();
        resourceTable.set(BeanService.KEY_PK_CODE, tableCode + "_ID");
        resourceTable.set(BeanService.KEY_TABLE_CODE, tableCode);
        for (Object keyObj : tableVals.keySet()) {
            String key = keyObj + "";
            resourceTable.set(key, tableVals.get(key));
        }

        List<Map<String, Object>> columnVals = metaService.selectSql("SELECT * FROM JE_CORE_TABLECOLUMN WHERE TABLECOLUMN_RESOURCETABLE_ID='" + resourceTable.getStr("JE_CORE_RESOURCETABLE_ID") + "' ORDER BY TABLECOLUMN_CLASSIFY,TABLECOLUMN_CODE");
        List<DynaBean> columns = new ArrayList<>();
        DynaBean column;
        for (Map columnVal : columnVals) {
            column = new DynaBean();
            column.set(BeanService.KEY_PK_CODE, "JE_CORE_TABLECOLUMN_ID");
            for (Object keyObj : columnVal.keySet()) {
                String key = keyObj + "";
                column.set(key, columnVal.get(key));
            }
            column.set(BeanService.KEY_TABLE_CODE, "JE_CORE_TABLECOLUMN");
            columns.add(column);
        }

        List<Map<String, Object>> keyVals = metaService.selectSql("SELECT * FROM JE_CORE_TABLEKEY WHERE TABLEKEY_RESOURCETABLE_ID='" + resourceTable.getStr("JE_CORE_RESOURCETABLE_ID") + "'  ORDER BY SY_ORDERINDEX");
        List<DynaBean> keys = new ArrayList<>();
        DynaBean key;
        for (Map keyVal : keyVals) {
            key = new DynaBean();
            key.set(BeanService.KEY_PK_CODE, "JE_CORE_TABLEKEY_ID");
            key.set(BeanService.KEY_TABLE_CODE, "JE_CORE_TABLEKEY");
            for (Object keyObj : keyVal.keySet()) {
                String keyV = keyObj + "";
                key.set(keyV, keyVal.get(keyV));
            }
            keys.add(key);
        }

        List<Map<String, Object>> indexVals = metaService.selectSql("SELECT * FROM JE_CORE_TABLEINDEX WHERE TABLEINDEX_RESOURCETABLE_ID='" + resourceTable.getStr("JE_CORE_RESOURCETABLE_ID") + "' ORDER BY SY_ORDERINDEX");
        List<DynaBean> indexs = new ArrayList<>();
        DynaBean index;
        for (Map indexVal : indexVals) {
            index = new DynaBean();
            index.set(BeanService.KEY_PK_CODE, "JE_CORE_TABLEKEY_ID");
            index.set(BeanService.KEY_TABLE_CODE, "JE_CORE_TABLEKEY");
            for (Object keyObj : indexVal.keySet()) {
                String keyV = keyObj + "";
                index.set(keyV, indexVal.get(keyV));
            }
            indexs.add(index);
        }
        resourceTable.set(BeanService.KEY_TABLE_COLUMNS, columns);
        resourceTable.set(BeanService.KEY_TABLE_KEYS, keys);
        resourceTable.set(BeanService.KEY_TABLE_INDEXS, indexs);
        return resourceTable;
    }

    @Override
    public String getFieldNames(DynaBean resourceTable, String[] excludes) {
        List<DynaBean> columns = (List<DynaBean>) resourceTable.get(BeanService.KEY_TABLE_COLUMNS);
        StringBuffer fieldCodes = new StringBuffer();
        for (DynaBean column : columns) {
            String columnCode = column.getStr("TABLECOLUMN_CODE");
            if (!ArrayUtils.contains(excludes, columnCode)) {
                fieldCodes.append(columnCode + ",");
            }
        }
        fieldCodes.deleteCharAt(fieldCodes.length() - 1);
        return fieldCodes.toString();
    }

    @Override
    public String getSysQueryFields(String tableCode) {
        DynaBean resourceTable = getResourceTable(tableCode);
        List<DynaBean> columns = (List<DynaBean>) resourceTable.get(BeanService.KEY_TABLE_COLUMNS);
        StringBuffer fieldCodes = new StringBuffer();
        for (DynaBean column : columns) {
            if ("SYS".equalsIgnoreCase(column.getStr("TABLECOLUMN_CLASSIFY"))) {
                String columnCode = column.getStr("TABLECOLUMN_CODE");
                fieldCodes.append(columnCode + ",");
            }
        }
        fieldCodes.deleteCharAt(fieldCodes.length() - 1);
        return fieldCodes.toString();
    }

    @Override
    public String getProQueryFields(String tableCode) {
        DynaBean resourceTable = getResourceTable(tableCode);
        List<DynaBean> columns = (List<DynaBean>) resourceTable.get(BeanService.KEY_TABLE_COLUMNS);
        StringBuffer fieldCodes = new StringBuffer();
        for (DynaBean column : columns) {
            if ("PRO".equalsIgnoreCase(column.getStr("TABLECOLUMN_CLASSIFY"))) {
                String columnCode = column.getStr("TABLECOLUMN_CODE");
                fieldCodes.append(columnCode + ",");
            }
        }
        fieldCodes.deleteCharAt(fieldCodes.length() - 1);
        return fieldCodes.toString();
    }

    @Override
    public String getQueryFields(String tableCode, String[] excludes) {
        DynaBean resourceTable = getResourceTable(tableCode);
        List<DynaBean> columns = (List<DynaBean>) resourceTable.get(BeanService.KEY_TABLE_COLUMNS);
        StringBuffer fieldCodes = new StringBuffer();
        for (DynaBean column : columns) {
            String columnCode = column.getStr("TABLECOLUMN_CODE");
            if (!ArrayUtils.contains(excludes, columnCode)) {
                fieldCodes.append(columnCode + ",");
            }
        }
        fieldCodes.deleteCharAt(fieldCodes.length() - 1);
        return fieldCodes.toString();
    }

    @Override
    public String getNoClobQueryFields(String tableCode) {
        DynaBean resourceTable = getResourceTable(tableCode);
        List<DynaBean> columns = (List<DynaBean>) resourceTable.get(BeanService.KEY_TABLE_COLUMNS);
        StringBuffer fieldCodes = new StringBuffer();
        for (DynaBean column : columns) {
            if (!ColumnType.CLOB.equalsIgnoreCase(column.getStr("TABLECOLUMN_TYPE")) || !ColumnType.BIGCLOB.equalsIgnoreCase(column.getStr("TABLECOLUMN_TYPE"))) {
                String columnCode = column.getStr("TABLECOLUMN_CODE");
                fieldCodes.append(columnCode + ",");
            }
        }
        fieldCodes.deleteCharAt(fieldCodes.length() - 1);
        return fieldCodes.toString();
    }

    @Override
    public JSONTreeNode getTreeTemplate(String tableCode) {
        DynaBean resourceTable = getResourceTable(tableCode);
        List<DynaBean> tableColumns = (List<DynaBean>) resourceTable.get(BeanService.KEY_TABLE_COLUMNS);
        JSONTreeNode template = buildJSONTreeNodeTemplate(tableColumns);
        return template;
    }

    @Override
    public JSONTreeNode buildJSONTreeNodeTemplate(List<DynaBean> columns) {
        JSONTreeNode node = new JSONTreeNode();
        for (DynaBean column : columns) {
            String code = column.getStr("TABLECOLUMN_CODE");
            String treeType = column.getStr("TABLECOLUMN_TREETYPE");
            if (treeType.equals(TreeNodeType.ID.toString())) {
                node.setId(code);
            } else if (treeType.equals(TreeNodeType.TEXT.toString())) {
                node.setText(code);
            } else if (treeType.equals(TreeNodeType.CODE.toString())) {
                node.setCode(code);
            } else if (treeType.equals(TreeNodeType.PARENT.toString())) {
                node.setParent(code);
            } else if (treeType.equals(TreeNodeType.NODEINFO.toString())) {
                node.setNodeInfo(code);
            } else if (treeType.equals(TreeNodeType.NODEINFOTYPE.toString())) {
                node.setNodeInfoType(code);
            } else if (treeType.equals(TreeNodeType.NODETYPE.toString())) {
                node.setNodeType(code);
            } else if (treeType.equals(TreeNodeType.ICON.toString())) {
                node.setIcon(code);
            } else if (treeType.equals(TreeNodeType.ICONCOLOR.toString())) {
                node.setIconColor(code);
            } else if (treeType.equals(TreeNodeType.DISABLED.toString())) {
                node.setDisabled(code);
            } else if (treeType.equals(TreeNodeType.NODEPATH.toString())) {
                node.setNodePath(code);
            } else if (treeType.equals(TreeNodeType.DESCRIPTION.toString())) {
                node.setDescription(code);
            } else if (treeType.equals(TreeNodeType.ORDERINDEX.toString())) {
                node.setOrderIndex(code);
            } else if (treeType.equals(TreeNodeType.LAYER.toString())) {
                node.setLayer(code);
            } else if (treeType.equals(TreeNodeType.TREEORDERINDEX.toString())) {
                node.setTreeOrderIndex(code);
            } else if (treeType.equals(TreeNodeType.OTHERBEANFIELD.toString())) {
                node.addOtherBeanFiled(code);
            }
        }
        return node;
    }
}
